package com.dbflow5.transaction;

import com.dbflow5.config.FlowLog;

import java.util.Iterator;
import java.util.concurrent.PriorityBlockingQueue;

/**
 * Description: Orders [Transaction] in a priority-based queue, enabling you perform higher priority
 * tasks first (such as retrievals) if you are attempting many DB operations at once.
 */
public class PriorityTransactionQueue extends Thread implements ITransactionQueue {

    public String name;

    private final PriorityBlockingQueue<PriorityEntry<Transaction<Object>>> queue = new PriorityBlockingQueue<>();

    private boolean isQuitting = false;

    public PriorityTransactionQueue(String name){
        super(name);
    }

    @Override
    public void run() {
        super.run();
        //Looper.prepare();
        //setThreadPriority(THREAD_PRIORITY_BACKGROUND);
        PriorityEntry<Transaction<Object>> transaction;
        while (true) {
            try {
                transaction = queue.take();
            } catch (InterruptedException e) {
                if (isQuitting) {
                    synchronized(queue) {
                        queue.clear();
                    }
                    return;
                }
                continue;
            }

            transaction.entry.executeSync();
        }
    }

    @Override
    public void add(Transaction<?> transaction) {
        synchronized(queue) {
            PriorityEntry<Transaction<Object>> priorityEntry = new PriorityEntry(transaction);
            if (!queue.contains(priorityEntry)) {
                queue.add(priorityEntry);
            }
        }
    }

    @Override
    public void cancel(Transaction<?> transaction) {
        synchronized(queue) {
            PriorityEntry<Transaction<?>> priorityEntry = new PriorityEntry<>(transaction);
            if (queue.contains(priorityEntry)) {
                queue.remove(priorityEntry);
            }
        }
    }

    @Override
    public void startIfNotAlive() {
        synchronized(this) {
            if (!isAlive()) {
                try {
                    start();
                } catch (IllegalThreadStateException i) {
                    // log if failure from thread is still alive.
                    FlowLog.log(FlowLog.Level.E, "", i);
                }

            }
        }
    }

    @Override
    public void cancel(String name) {
        synchronized(queue) {
            Iterator<PriorityEntry<Transaction<Object>>> it = queue.iterator();
            while (it.hasNext()) {
                Transaction<Object> next = it.next().entry;
                if (next.name != null && next.name.equals(name)) {
                    it.remove();
                }
            }
        }
    }

    @Override
    public void quit() {
        isQuitting = true;
        interrupt();
    }

    static class PriorityEntry<E extends Transaction<?>> implements Comparable<PriorityEntry<Transaction<?>>> {
        private PriorityTransactionWrapper transactionWrapper;
        E entry;

        PriorityEntry(E entry){
            this.entry = entry;
            init();
        }

        private void init() {
            if(entry.transaction instanceof PriorityTransactionWrapper) {
                transactionWrapper = (PriorityTransactionWrapper)(entry.transaction);
            }else {
                transactionWrapper = PriorityTransactionWrapper.withPriority(entry.transaction, PriorityTransactionWrapper.PRIORITY_NORMAL);
            }
        }

        @Override
        public int compareTo(PriorityEntry<Transaction<?>> other) {
            return transactionWrapper.compareTo(other.transactionWrapper);
        }
    }
}
